-- Scheme for Stalkers to hide from combative helicopters --[[ scheme_type: generic author: Alundaio modified_by: --]] -------------------- evaid = 188117 actid = 188117 -------------------- local math_random = math.random ------------------------------------------------------------------------------------- -- PRIVATE -------------------------------------------------------------------------------------- local function validate(npc,vid) if (vid and vid < 4294967295 and npc:accessible(vid) and vid ~= npc:level_vertex_id()) then return db.used_level_vertex_ids[vid] == nil or db.used_level_vertex_ids[vid] == npc:id() end return false end local function lmove(npc,vid,st) if (vid == nil or vid >= 4294967295) then return end if (db.used_level_vertex_ids[vid] == npc:id()) then npc:set_dest_level_vertex_id(vid) return vid end if (st.vid) then db.used_level_vertex_ids[st.vid] = nil end if not (npc:accessible(vid)) then local vtemp = VEC_ZERO vid, vtemp = npc:accessible_nearest(vid and level.vertex_position(vid) or npc:position(), vtemp ) end db.used_level_vertex_ids[vid] = npc:id() st.vid = vid npc:set_dest_level_vertex_id(vid) return vid end local function lmove_ns(npc,vid,st) if (vid == nil or vid >= 4294967295) then return end if (db.used_level_vertex_ids[vid] == npc:id()) then npc:set_dest_level_vertex_id(vid) return vid end if (st.vid) then db.used_level_vertex_ids[st.vid] = nil end if not (npc:accessible(vid)) then local vtemp = VEC_ZERO vid, vtemp = npc:accessible_nearest(vid and level.vertex_position(vid) or npc:position(), vtemp ) end st.vid = vid npc:set_dest_level_vertex_id(vid) return vid end local function best_heli(npc) local sim = alife() local se_heli for id,o in pairs(db.heli) do local dist = heli_alife.distance_2d_sqr(npc:position(),o:position()) if (dist < 56000 and db.storage[id] and db.storage[id].enemy_id) then se_heli = sim:object(o:id()) if (se_heli and se_heli.community and se_heli.community ~= character_community(npc)) then return o end end end end -------------------------------------------------------------------------------------- -- EVALUATOR HIDE FROM HELI -------------------------------------------------------------------------------------- class "evaluator_npc_vs_heli" (property_evaluator) function evaluator_npc_vs_heli:__init(npc,name,storage) super (nil, name) self.st = storage self.st.stage = 0 end function evaluator_npc_vs_heli:evaluate() --utils_data.debug_write("eva_npc_vs_heli") local npc = self.object if not (npc:alive()) then return false end if not (npc:has_info("npcx_is_companion")) then if (get_object_story_id(npc:id())) then return false end end if (xr_conditions.surge_started()) then return false end if (IsWounded(npc)) then return false end local st = db.storage[npc:id()] if (st) and (st.active_scheme == "camper" or st.rx_dont_shoot or st.help_wounded and st.help_wounded.selected_id ~= nil) then return false end if (st.panicked) then return false end if (npc:has_info("npcx_beh_ignore_combat")) then return false end local be = npc:best_enemy() if (be) then if (xr_danger.has_danger(npc)) then return false end local a = npc:memory_time(be) if (a and time_global() - a < 10000 and npc:position():distance_to(npc:memory_position(be)) < 10) then return false end end local bd = npc:best_danger() if (bd and bd:type() == danger_object.grenade and db.storage[npc:id()].danger_flag) then return false end if not (best_heli(npc)) then return false end return true end -------------------------------------------------------------------------------------- -- ACTION HIDE FROM HELI -------------------------------------------------------------------------------------- class "action_npc_vs_heli" (action_base) function action_npc_vs_heli:__init (npc,name,storage) super (nil,name) self.st = storage self.st.__node_stage = 1 end function action_npc_vs_heli:initialize() action_base.initialize(self) end function action_npc_vs_heli:try_go_cover(npc,ene_pos,r) --utils_data.debug_write(strformat("action_npc_vs_heli:try_go_cover")) local lvid = load_var(npc,"fight_from_point") local pos = lvid and level.vertex_position(lvid) or npc:position() local vid = utils_obj.find_close_cover(npc,pos,ene_pos,r or 3,1,50) if (validate(npc,vid)) then return lmove(npc,vid,self.st) end end function action_npc_vs_heli:try_go_backward(npc) --utils_data.debug_write(strformat("action_npc_vs_heli:try_go_backward")) local dir = vector_rotate_y(vec_set(npc:direction()),math_random(160,200)) local vid local radius = 8 local base_point = npc:level_vertex_id() while (radius > 0) do vid = level.vertex_in_direction(base_point,dir,radius) if (vid and vid < 4294967295 and npc:accessible(vid) and vid ~= npc:level_vertex_id()) then return lmove_ns(npc,vid,self.st) end radius = radius - 1 end end function action_npc_vs_heli:try_to_strafe(npc) --utils_obj.debug_nearest(npc,"try_to_strafe") local dir = {} dir[1] = vector_rotate_y(vec_set(npc:direction()),math.random(-87,-93)) dir[2] = vector_rotate_y(vec_set(npc:direction()),math.random(87,93)) local vid local radius local base_point = npc:level_vertex_id() for i=1,2 do radius = 4 while (radius > 1) do vid = level.vertex_in_direction(base_point,dir[i],radius) if (vid and vid < 4294967295 and npc:accessible(vid) and vid ~= npc:level_vertex_id()) then return lmove_ns(npc,vid,self.st) end radius = radius - 1 end end end function action_npc_vs_heli:execute() --utils_data.debug_write(strformat("action_npc_vs_heli:execute start")) action_base.execute(self) local npc = self.object local enemy = best_heli(npc) if not (enemy) then return end local heli = enemy:get_helicopter() if not (heli) then return end local be = npc:best_enemy() -- ensure and enforce path type if (npc:path_type() ~= game_object.level_path) then npc:set_path_type(game_object.level_path) end local see = be and npc:see(be) or not be and heli:isVisible(npc) local ene_pos = be and see and be:position() or be and npc:memory_position(be) or enemy:position() local heli_ene_id = db.storage[enemy:id()] and db.storage[enemy:id()].enemy_id local heli_ene if (heli_ene_id) then heli_ene = db.storage[heli_ene_id] and db.storage[heli_ene_id].object end local bw = npc:active_item() local can_fight = bw and IsWeapon(bw) and (npc:has_info("npcx_is_companion") or be ~= nil or character_community(npc) == "monolith" or (IsSniper(bw) or IsLauncher(bw) or IsRifle(bw))) or false if (be == nil and can_fight) then can_fight = heli_ene and character_community(npc) == character_community(heli_ene) or db.storage[npc:id()].heli_enemy_flag == true end local tg = time_global() if (self.st.vid) then if (can_fight) and (self.st.__keep_body_state_until==nil or tg > self.st.__keep_body_state_until) then local dir = ene_pos and vec_sub(ene_pos,npc:position()) or npc:direction() self.st.__keep_body_state_until = tg+5000 if (npc:body_state() ~= move.crouch) then --printf("stand=%s",level.high_cover_in_direction(npc:level_vertex_id(),dir)) if (level.high_cover_in_direction(npc:level_vertex_id(),dir) < 0.2) then self.stand = false end else --printf("crouch=%s",level.high_cover_in_direction(npc:level_vertex_id(),dir)) if (level.high_cover_in_direction(npc:level_vertex_id(),dir) >= 0.2) then self.stand = true end end end local new_state if (npc:level_vertex_id() == self.st.vid) then if not (can_fight) then new_state = "hide_no_wpn" else local is_sniper = IsSniper(bw) if (is_sniper == nil and self.stand) then new_state = see and (is_sniper and "threat_sniper_fire" or "threat_fire") or "threat" else new_state = see and (is_sniper and "hide_sniper_fire" or "sneak_fire") or "sneak_run" end end if (see and can_fight) then if (be) then state_mgr.set_state(npc,new_state,nil,nil,{look_object = be},{fast_set = true}) else local dir = ene_pos and vec_sub(ene_pos,utils_obj.safe_bone_pos(npc,"bip01_r_finger02")) or nil state_mgr.set_state(npc,new_state,nil,nil,{look_object = enemy, look_dir = dir},{fast_set = true}) end else state_mgr.set_state(npc,new_state,nil,nil,nil,{fast_set = true}) end self.st.__keep_point_until = self.st.__keep_point_until or tg + math.random(3000,8000) if (self.st.__keep_point_until and tg < self.st.__keep_point_until) then return end self.st.__keep_point_until = nil db.used_level_vertex_ids[self.st.vid] = nil self.st.vid = nil else if not (can_fight) then new_state = "sneak_run_no_wpn" else local is_sniper = IsSniper(bw) if (self.stand) then if (be) then new_state = see and (is_sniper and "threat_sniper_fire" or "assault_fire") or "assault" else new_state = "assault" end else if (be) then new_state = see and (is_sniper and "hide_sniper_fire" or "sneak_fire") or "sneak_run" else new_state = "sneak_run" end end end if (see and can_fight) then if (be) then state_mgr.set_state(npc,new_state,nil,nil,{look_object = be},{fast_set = true}) else local dir = ene_pos and vec_sub(ene_pos,utils_obj.safe_bone_pos(npc,"bip01_r_finger02")) or nil state_mgr.set_state(npc,new_state,nil,nil,{look_object = enemy, look_dir = dir},{fast_set = true}) end else state_mgr.set_state(npc,new_state,nil,nil,nil,{fast_set = true}) end lmove(npc,self.st.vid,self.st) return end end if (self:try_go_cover(npc,be and ene_pos or heli and enemy:position() or npc:position())) then else self:try_to_strafe(npc) end --utils_data.debug_write(strformat("action_npc_vs_heli:execute end")) end function action_npc_vs_heli:finalize() action_base.finalize(self) if (self.st.vid) then db.used_level_vertex_ids[self.st.vid] = nil end end ---------------------------------------------------------------------------------------------------------------------- -- BINDER ---------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------- --------------------------------- BINDER --------------------------- function setup_generic_scheme(npc,ini,scheme,section,stype,temp) local st = xr_logic.assign_storage_and_bind(npc,ini,"npc_vs_heli",section,temp) end function add_to_binder(npc,ini,scheme,section,storage,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager then return end if (not npc:alive()) or (npc:character_community() == "zombied" or npc:section() == "actor_visual_stalker") then manager:add_evaluator(evaid,property_evaluator_const(false)) --temp.needs_configured = false else manager:add_evaluator(evaid,evaluator_npc_vs_heli(npc,"eva_npc_vs_heli",storage)) end temp.action = action_npc_vs_heli(npc,"act_npc_vs_heli",storage) temp.action:add_precondition(world_property(stalker_ids.property_alive,true)) temp.action:add_precondition(world_property(stalker_ids.property_danger, false)) temp.action:add_precondition(world_property(evaid,true)) temp.action:add_effect(world_property(evaid,false)) manager:add_action(actid,temp.action) xr_logic.subscribe_action_for_events(npc, storage, temp.action) end function configure_actions(npc,ini,scheme,section,stype,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager or not temp.action then return end temp.action:add_precondition(world_property(xr_evaluators_id.sidor_wounded_base,false)) temp.action:add_precondition(world_property(xr_evaluators_id.wounded_exist,false)) if (_G.schemes["rx_ff"]) then temp.action:add_precondition(world_property(rx_ff.evaid,false)) end if (_G.schemes["gl"]) then temp.action:add_precondition(world_property(rx_gl.evid_gl_reload,false)) end if (_G.schemes["facer"]) then temp.action:add_precondition(world_property(xrs_facer.evid_facer,false)) temp.action:add_precondition(world_property(xrs_facer.evid_steal_up_facer,false)) end if (_G.schemes["npc_vs_box"]) then temp.action:add_precondition(world_property(axr_npc_vs_box.evaid,false)) end local manager = npc:motivation_action_manager() local action local p = { xr_danger.actid, stalker_ids.action_combat_planner, stalker_ids.action_danger_planner, xr_actions_id.state_mgr + 1, xr_actions_id.state_mgr + 2, xr_actions_id.alife } for i=1,#p do action = manager:action(p[i]) if (action) then action:add_precondition(world_property(evaid,false)) else printf("axr_npc_vs_heli: no action id p[%s]",i) end end end function disable_generic_scheme(npc,scheme,stype) local st = db.storage[npc:id()][scheme] if st then st.enabled = false end end function npc_add_precondition(action) if not action then return end action:add_precondition(world_property(evaid,false)) end function is_under_npc_vs_heli(npc) local mgr = npc:motivation_action_manager() if not mgr or not mgr:initialized() then return false end local current_action_id = mgr:current_action_id() return current_action_id == actid end